package com.knight.aop;


import com.alibaba.fastjson.JSON;
import com.knight.annotation.KnightLog;
import com.knight.model.Log;
import com.knight.service.LogService;
import com.knight.threadpool.SaveLogTask;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.ExecutorService;

@Aspect
@Component
public class KnightLogAspect {

    private static Logger logger = LoggerFactory.getLogger(KnightLogAspect.class);

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private ExecutorService cachedThreadPool;

    @Autowired
    private LogService logService;

    /**
     * 定义切点
     */
    @Pointcut("@annotation(com.knight.annotation.KnightLog)")
    public void xxx() {

    }


    @Around("xxx()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Log log = new Log();
        log.setLogId(UUID.randomUUID().toString());

        Object[] args = joinPoint.getArgs(); //入参数
        String params = JSON.toJSONString(args);
        log.setParams(params);

        String ip = getClientIP(request);//客户端IP
        log.setRemoteAddr(ip);

        String requestUri = request.getRequestURI();// 请求的Uri
        log.setUri(requestUri);

        String method = request.getMethod();// 请求的方法类型(post/get)
        log.setMethod(method);

        String title = getMethodDesc(joinPoint); //方法描述
        log.setTitle(title);

        long beginTime = System.currentTimeMillis();//开始时间
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        log.setStartTime(new Date());

        Object result;
        try {
            result = joinPoint.proceed();
        }catch (Exception e) {
            log.setType("error");
            log.setException(e.getMessage());
            log.setExecuteTime(System.currentTimeMillis()-beginTime);//执行时间
            cachedThreadPool.execute(new SaveLogTask(logService,log));
            throw e;
        }
        log.setExecuteTime(System.currentTimeMillis()-beginTime);//执行时间
        log.setType("info");
        log.setResultParams(JSON.toJSONString(result));
        cachedThreadPool.execute(new SaveLogTask(logService,log));

        return result;
    }





    /**
     * 获取注解中对方法的描述信息
     *
     * @param joinPoint 切点
     * @return desc
     */
    public static String getMethodDesc(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        KnightLog controllerLog = method.getAnnotation(KnightLog.class);
        return controllerLog.desc();
    }


    /***
     * 获取客户端IP地址;这里通过了Nginx获取;X-Real-IP,
     * @param request
     * @return
     */
    public static String getClientIP(HttpServletRequest request) {
        String fromSource = "X-Real-IP";
        String ip = request.getHeader("X-Real-IP");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
            fromSource = "X-Forwarded-For";
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
            fromSource = "Proxy-Client-IP";
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
            fromSource = "WL-Proxy-Client-IP";
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            fromSource = "request.getRemoteAddr";
        }

        return ip;
    }
}
